iT邦幫忙

2023 iThome 鐵人賽

DAY 26
0
Modern Web

JS30 x 鐵人30 x MDN doc系列 第 26

[Day26] - Stripe Follow Along Nav(JS30 x 鐵人 30 x MDN)

  • 分享至 

  • xImage
  •  

做一個 nav-bar 當使用者 hover 時會展開下拉式清單,搭配動畫緩進緩出

  1. 觀察 index-Start.html 可以發現作者已將所有下拉式選單.dropdown的內容都透過 css 隱藏起來了。
.dropdown {
  /* 透明度0 */
  opacity: 0;
  position: absolute;
  overflow: hidden;
  padding: 20px;
  top: -20px;
  border-radius: 2px;
  transition: all 0.5s;
  transform: translateY(100px);
  will-change: opacity;
  /* 排版:隱藏 */
  display: none;
}

而下方又有這兩組 css,代表的是當外層容器有trigger-entertrigger-enter-active時選單要變更排版及透明度設定。

.trigger-enter .dropdown {
  display: block;
}
.trigger-enter-active .dropdown {
  opacity: 1;
}

負責下拉選單背景的.dropdownBackground容器也有設定透明度隱藏,及若有.openclass 時解除隱藏

.dropdownBackground {
  /*  .               */
  /*  .  其餘css設定   */
  /*  .              */
  opacity: 0;
}

.dropdownBackground.open {
  opacity: 1;
}

因此我們要做的是即是在適當的位置添加事件監聽器,當滑鼠滑入時加入 class 當滑鼠移出時移除 class,看一下結構我們選擇使用<ul class="cool">清單下的每個<li>

//  選取每個nav li 節點
const li = document.querySelectorAll(".cool>li");
//  選取白框背景容器
const background = document.querySelector(".dropdownBackground");

// 每個都增加滑鼠滑入監聽器及滑鼠移出監聽器
li.forEach((node) => {
  node.addEventListener("mouseenter", handleMouseenter);
});
li.forEach((node) => {
  node.addEventListener("mouseleave", handleMouseleave);
});
  1. 我們在對應的 handle 函式新增、移除指定 class 看看效果
function handleMouseenter(e) {
  this.classList.add("trigger-enter", "trigger-enter-active");
  background.classList.add("open");
}
function handleMouseleave(e) {
  this.classList.remove("trigger-enter", "trigger-enter-active");
  background.classList.remove("open");
}

  1. 效果是我們要的,最後只要改寫handleMouseenter函式將背景容器的寬高變成觸發事件的下拉式選單寬高,以及位移背景容器到觸發事件的下拉式選單位置,即可以達到題目要求效果。
//  全域新增兩個變數,因為nav上有設定position:relative;
//  所以會造成位置偏移要扣掉nav.getBoundingClientRect的top及left
const nav = document.querySelector(".top");
const navCoords = nav.getBoundingClientRect();

//  改寫`handleMouseenter`函式
function handleMouseenter(e) {
  this.classList.add("trigger-enter");
  //  因為dropdownBackgroundb容器css有設定transition,所以opacity恢復透明度需要延遲一下
  setTimeout(() => {
    this.classList.add("trigger-enter-active");
  }, 150);
  background.classList.add("open");
  //  選取觸發事件容器內的下拉式選單
  const dropdown = this.querySelector(".dropdown");
  //  display取消隱藏後,才能取得邊界矩形
  const dropdownCoords = dropdown.getBoundingClientRect();
  const coords = {
    height: dropdownCoords.height,
    width: dropdownCoords.width,
    //  因為nav上有設定position:relative;
    //  所以會造成位置偏移要扣掉nav.getBoundingClientRect的top及left
    top: dropdownCoords.top - navCoords.top,
    left: dropdownCoords.left - navCoords.left,
  };
  //  將背景容器寬高,及位置依序設定
  background.style.setProperty("width", `${coords.width}px`);
  background.style.setProperty("height", `${coords.height}px`);
  background.style.setProperty(
    "transform",
    `translate(${coords.left}px, ${coords.top}px)`
  );
}

👉Github Demo 頁面 👈

👉 好想工作室 15th 鐵人賽看板 👈

參考資料

  1. Javascript 30 官網
    https://javascript30.com/
  2. MDN 官網
    https://developer.mozilla.org/en-US/

上一篇
[Day25] - Event Capture, Propagation, Bubbling and Once(JS30 x 鐵人 30 x MDN)
下一篇
[Day27] - Click and Drag(JS30 x 鐵人 30 x MDN)
系列文
JS30 x 鐵人30 x MDN doc30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言